package it.tukano.swingdeco.tree;

import it.tukano.collections.ListExt;
import it.tukano.fun.Function1;
import it.tukano.fun.Function2;
import it.tukano.fun.VFunction1;
import it.tukano.fun.VFunction3;
import it.tukano.swingdeco.containers.JPanelExt;
import it.tukano.swingdeco.controls.JButtonExt;
import it.tukano.swingdeco.draganddrop.DndUtilities;
import it.tukano.swingdeco.input.PopupInput;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.Map;
import javax.swing.JScrollPane;
import javax.swing.tree.DefaultMutableTreeNode;

/**
 * A predefined folder's-tree
 * @author pgi
 */
public class StandardFolderTree extends JPanelExt {

    /* Serial version of this class */
    private static final long serialVersionUID = 0;

    /**
     * This button controls the creation of a new node
     */
    public JButtonExt newNode = new JButtonExt("New").scaleFont(0.8).onActionPerformed(new Runnable() {

        public void run() {
            StandardFolderTree.this.createNewNode();
        }
    });
    /**
     * This button changes the name of a node
     */
    public JButtonExt renameNode = new JButtonExt("Rename").scaleFont(0.8).onActionPerformed(new Runnable() {

        public void run() {
            StandardFolderTree.this.renameSelectedNode();
        }
    }).setDisabled();
    /**
     * This buttons moves the selected node to the thrash folder (if any), empties the thrash folder or removes the selected node
     */
    public JButtonExt thrashSelectedNode = new JButtonExt("Thrash").scaleFont(0.8).onActionPerformed(new Runnable() {

        public void run() {
            StandardFolderTree.this.thrashSelectedNode();
        }
    }).setDisabled();
    /**
     * This panel displayes the control buttons
     */
    public JPanelExt buttonsBar = new JPanelExt(new GridLayout(1, 3)).append(newNode).append(renameNode).append(thrashSelectedNode);
    /**
     * The folder's tree
     */
    public JTreeExt tree = new JTreeExt().setClearSelectionOnClick().onSelectionChange(new VFunction1<DefaultMutableTreeNode>() {

        @Override protected void doApply(DefaultMutableTreeNode p) {
            if(p == null || p == tree.getRoot()) {
                newNode.setEnabled();
                renameNode.setDisabled();
                thrashSelectedNode.withText("Thrash").setDisabled();
            } else {
                DefaultMutableTreeNode thrashNode = getThrashNode();
                if(p == thrashNode) {
                    newNode.setDisabled();
                    renameNode.setDisabled();
                    thrashSelectedNode.withText("Empty").setEnabled(thrashNode.getChildCount() != 0);
                } else {
                    newNode.setEnabled();
                    renameNode.setEnabled();
                    thrashSelectedNode.withText("Thrash").setEnabled();
                }
            }
        }
    });
    /**
     * The tree scroller
     */
    public JScrollPane scroller = tree.getScroller();
    /**
     * If != null, this function will be called to handle the creation of a new node. The function will receive the name of
     * the node to create.
     */
    public Function1<String, ?> createNewNode;
    /**
     * If != null, this function will be called to handle the renaming of the selected node. The function will receive the new
     * name of the node. The renamed node is the selected node in the tree
     */
    public Function1<String, ?> renameSelectedNode;
    /**
     * If != null, this function marks a node in the tree as the "thrash" node. If there is a thrash node, removing a node will
     * transfer it into the thrash and "thrashing" the thrash will finally delete it
     */
    public Function1<JTreeExt, DefaultMutableTreeNode> getThrashNode;

    /**
     * Instance initializer
     */
    public StandardFolderTree() {
        super(new BorderLayout());
        append(buttonsBar, BorderLayout.NORTH, scroller, BorderLayout.CENTER);
    }

    public StandardFolderTree enableNodeDragAndDrop(final boolean dropOnRoot) {
        DndUtilities.installLocalObjectDragHandler(tree, Integer.class, new Function2<Component, Point, Integer>() {

            public Integer apply(Component a, Point b) {
                JTreeExt tree = (JTreeExt) a;
                DefaultMutableTreeNode target = tree.getNodeAt(b).get();
                DefaultMutableTreeNode thrashNode = getThrashNode();
                if(target == null) return null;
                if(thrashNode != null && thrashNode == target) return null;
                return target.hashCode();
            }
        });
        DndUtilities.installLocalObjectDropHandler(tree, ListExt.<Class<?>>newArrayListExt().append(Integer.class), new VFunction3<Component, Point, Map<Class<?>, Object>>() {

            @Override
            protected void doApply(Component p1, Point p2, Map<Class<?>, Object> p3) {
                if(p3.containsKey(Integer.class)) {
                    JTreeExt tree = (JTreeExt) p1;
                    DefaultMutableTreeNode dropNode = tree.getNodeAt(p2).get();
                    if(dropNode == null && dropOnRoot) dropNode = tree.getRoot();
                    if(dropNode != null) {
                        DefaultMutableTreeNode draggedNode = null;
                        Integer childHash = (Integer) p3.get(Integer.class);
                        for (DefaultMutableTreeNode object : tree) {
                            if(object.hashCode() == childHash) {
                                draggedNode = object;
                                break;
                            }
                        }
                        if(draggedNode != null) tree.move(draggedNode, dropNode);
                    }
                }
            }
        });
        return this;
    }

    protected void createNewNode() {
        if(createNewNode != null) PopupInput.showInputStringPopup(newNode, "Insert a name", 20, createNewNode);
    }

    protected void renameSelectedNode() {
        if(renameSelectedNode != null) PopupInput.showInputStringPopup(renameNode, "Insert a name", 20, renameSelectedNode);
    }

    protected void thrashSelectedNode() {
        final DefaultMutableTreeNode thrashNode = getThrashNode();
        DefaultMutableTreeNode selectedNode = tree.getSelectedNode().get();
        if(thrashNode != null && selectedNode == thrashNode) {
            PopupInput.showConfirmPopup(thrashSelectedNode, "Remove all the elements in the thrash folder?", new Runnable() {

                public void run() {
                    thrashNode.removeAllChildren();
                    thrashSelectedNode.setEnabled(false);//thrash is empty
                    tree.update(thrashNode);
                }
            }, null);
        } else {
            if(thrashNode != null) {
                tree.move(tree.getSelectedNode().get(), thrashNode);
            } else {
                PopupInput.showConfirmPopup(thrashSelectedNode, "Remove the selected element?", new Runnable() {

                    public void run() {
                        tree.remove(tree.getSelectedNode().get());
                    }
                }, null);
            }
        }
    }

    protected DefaultMutableTreeNode getThrashNode() {
        if(getThrashNode != null) {
            return getThrashNode.apply(tree);
        } else {
            return null;
        }
    }
}
